home *** CD-ROM | disk | FTP | other *** search
- /*
- The animation guts from a freely distributable X program:
- xsperm.c
- Drew Olbrich, Febrary 1991
- Note -- This code originally served as a demonstration
- of how to do animation under X. The "guts" of the program
- which draws the sperm are consequently located in one huge
- chunk in the update_display() routine, and can be easily
- cut out.
-
- The animation function wrapped in a NeXTstep View subclass by Ali Ozer, May 91
- Very minor changes so this thing works as a screen saver module by sam streeper,
- August 91
- The "oneStep" method computes new locations.
- */
-
- #import "SpermView.h"
- #import "Thinker.h"
- #import <appkit/appkit.h>
-
- #define VEC_DOT(x, y) (x[0]*y[0] + x[1]*y[1])
- #define VEC_LEN(x) (sqrt(x[0]*x[0] + x[1]*x[1]))
-
- #define VEC_SET(x, a, b) x[0] = a, x[1] = b
- #define VEC_COPY(y, x) y[0] = x[0], y[1] = x[1]
- #define VEC_NEG(x) x[0] = -x[0], x[1] = -x[1]
- #define VEC_ADD(z, x, y) z[0] = x[0] + y[0], z[1] = x[1] + y[1]
- #define VEC_SUB(z, x, y) z[0] = x[0] - y[0], z[1] = x[1] - y[1]
- #define VEC_MULT(x, a) x[0] *= a, x[1] *= a
- #define VEC_DIV(x, a) x[0] /= a, x[1] /= a
- #define VEC_ADDS(z, x, a, y) z[0] = x[0] + (a)*y[0], z[1] = x[1] + (a)*y[1]
- #define VEC_NORM(x) { double l = VEC_LEN(x); VEC_DIV(x, l); }
-
- #define MINRAD 0.1
- #define RADSTEP 2.0
- #define MAXRAD (MINRAD * RADSTEP * RADSTEP * RADSTEP * RADSTEP * RADSTEP * RADSTEP)
- #define INITRAD (MINRAD * RADSTEP * RADSTEP * RADSTEP)
-
- // RANDINT(n) returns an integer 0..n-1
- // RANDFLOAT(f) returns a float [0..f] (inclusive on both ends)
-
- #define RANDINT(n) (random() % (n))
- #define RANDFLOAT(f) (((f) * (float)(random() & 0x0ffff)) / (float)0x0ffff)
-
- @implementation SpermView
-
- - initFrame:(const NXRect *)rect
- {
- [super initFrame:rect];
-
- [self allocateGState]; // For faster lock/unlockFocus
-
- dir = 1.0;
- rad = INITRAD;
- [self getSpermCount];
- [self getLineWidth];
- [self getUseColor];
- [inspectorPanel display];
-
- color = NX_COLORWHITE;
- alreadyInitialized = NO;
- randCount1 = 100;
- randCount2 = 200;
-
- uPath = newUserPath();
-
- return self;
- }
-
- - (void)initializeLine:(int)i
- {
- double angle = RANDFLOAT(10.0) + 5.0;
- prevX[i][0] = x[i][0] = (double) (RANDINT((int)NX_WIDTH(&bounds)));
- prevX[i][1] = x[i][1] = (double) (RANDINT((int)NX_HEIGHT(&bounds)));
- v[i][0] = RANDFLOAT(2.0) - 1.0;
- v[i][1] = RANDFLOAT(2.0) - 1.0;
- sine[i] = sin(angle*M_PI/180.0);
- cosine[i] = cos(angle*M_PI/180.0);
- vel[i] = RANDFLOAT(4.0) + 4.0;
- VEC_NORM(v[i]);
- }
-
- - (void)getFocusFromEvent:(NXEvent *)event
- {
- NXPoint loc = event->location;
- [self convertPoint:&loc fromView:nil];
- mouse[0] = loc.x;
- mouse[1] = loc.y;
- }
-
- - (BOOL)acceptsFirstMouse
- { return YES;
- }
-
- - mouseDown:(NXEvent *)event
- {
- [self getFocusFromEvent:event];
- return self;
- }
-
- - effectOne
- {
- VECTOR y;
- int i;
-
- dir *= -1.0;
- for (i = 0; i < MAXCOUNT; i++) {
- VEC_COPY(y, v[i]);
- if (dir == -1.0) {
- v[i][0] = y[1];
- v[i][1] = -y[0];
- } else {
- v[i][0] = -y[1];
- v[i][1] = y[0];
- }
- }
- return self;
- }
-
- - effectTwo
- {
- int i;
- for (i = 0; i < MAXCOUNT; i++) {
- v[i][0] = -v[i][0];
- }
- return self;
- }
-
- - effectThree
- {
- int i;
- for (i = 0; i < MAXCOUNT; i++) {
- v[i][1] = -v[i][1];
- }
- return self;
- }
-
- - effectFour
- {
- [self effectTwo];
- [self effectThree];
- return self;
- }
-
- - effectFive
- {
- [self effectOne];
- [self effectFour];
- return self;
- }
-
- - effectSix
- {
- rad = MIN(rad * RADSTEP, MAXRAD);
- return self;
- }
-
- - effectSeven
- {
- rad = MAX(rad / RADSTEP, MINRAD);
- return self;
- }
-
- - doEffectNumber:(int)val
- {
- switch (val) {
- case 0: [self effectOne]; break;
- case 1: [self effectTwo]; break;
- case 2: [self effectThree]; break;
- case 3: [self effectFour]; break;
- case 4: [self effectFive]; break;
- case 5: [self effectSix]; break;
- case 6: [self effectSeven]; break;
- default: break;
- }
- return self;
- }
-
- - oneStep
- {
- int i, cnt;
- POINT lLeft, uRight;
- NXRect eraseRect;
-
- uRight[0] = lLeft[0] = x[0][0];
- uRight[1] = lLeft[1] = x[0][1];
-
- for (i = 0; i < count; i++) {
- VECTOR w, y;
- POINT p;
- double r;
-
- for (cnt = 0; cnt < 2; cnt++) {
- if (prevX[i][cnt] < lLeft[cnt]) lLeft[cnt] = prevX[i][cnt];
- else if (prevX[i][cnt] > uRight[cnt]) uRight[cnt] = prevX[i][cnt];
- if (x[i][cnt] < lLeft[cnt]) lLeft[cnt] = x[i][cnt];
- else if (x[i][cnt] > uRight[cnt]) uRight[cnt] = x[i][cnt];
- }
-
- prevX[i][0] = x[i][0]; /* old location */
- prevX[i][1] = x[i][1];
-
- VEC_SUB(w, x[i], mouse);
- VEC_NORM(w);
- VEC_COPY(y, w);
- w[0] = y[0]*cosine[i] - dir*y[1]*sine[i];
- w[1] = y[1]*cosine[i] + dir*y[0]*sine[i];
- VEC_ADDS(p, mouse, rad*(160.0 - vel[i]*20.0), w);
-
- VEC_SUB(w, p, x[i]);
- r = VEC_LEN(w);
- VEC_DIV(w, r);
-
- VEC_ADDS(v[i], v[i], 1.0, w);
-
- VEC_NORM(v[i]);
- VEC_MULT(v[i], vel[i]);
-
- VEC_ADD(x[i], x[i], v[i]);
-
- }
-
- NXSetRect (&eraseRect, lLeft[0], lLeft[1], uRight[0]-lLeft[0], uRight[1]-lLeft[1]);
- NXInsetRect (&eraseRect, -1.0-lineWidth, -1.0-lineWidth);
- PSsetgray(0);
- NXRectFill(&eraseRect);
-
- [self drawPath];
-
- if (--randCount1 < 0)
- {
- randCount1 = RANDINT(700);
- mouse[0] = randBetween(0,bounds.size.width);
- mouse[1] = randBetween(0,bounds.size.height);
- }
- if (--randCount2 < 0)
- {
- randCount2 = RANDINT(600);
- [self doEffectNumber:(randCount2 % 7)];
- }
- return self;
- }
-
- // Modify "orig" by upto plus or minus "by" keeping it in the specified range...
-
- static float randMod(float orig, float by, float min, float max)
- {
- orig = orig + RANDFLOAT(by * 2.0) - by;
- return (orig < min) ? min : ((orig > max) ? max : orig);
- }
-
- - drawPath
- {
- int cnt;
-
- PSsetlinewidth (lineWidth);
- if (useColors) {
- color = NXConvertRGBToColor(randMod(NXRedComponent(color), 0.05, 0.0, 1.0),
- randMod(NXGreenComponent(color), 0.05, 0.0, 1.0),
- randMod(NXBlueComponent(color), 0.05, 0.0, 1.0));
- }
- else color = NX_COLORWHITE;
-
- NXSetColor (color);
-
- beginUserPath(uPath, NO);
- for (cnt = 0; cnt < count; cnt++) {
- UPmoveto(uPath, (float)prevX[cnt][0], (float)prevX[cnt][1]);
- UPlineto(uPath, (float)x[cnt][0], (float)x[cnt][1]);
- }
- closePath(uPath);
- endUserPath(uPath, dps_ustroke);
- sendUserPath(uPath);
-
- return self;
- }
-
- - drawSelf:(const NXRect *)rects :(int)rectCount
- {
- if (!rects || !rectCount) return self;
-
- PSsetgray(NX_BLACK);
- NXRectFill(rects);
- [self drawPath];
- return self;
- }
-
- - newWindow
- {
- mouse[0] = randBetween(0,bounds.size.width);
- mouse[1] = randBetween(0,bounds.size.height);
-
- return self;
- }
-
- - free
- {
- freeUserPath(uPath);
- return [super free];
- }
-
- - setNumLines:sender
- {
- int i;
- int oldCount = count;
- char str[100];
-
- // set the number of lines
- count = MIN(MAXCOUNT, MAX([sender intValue], 1));
-
- // initialize velocities & such
- for (i = oldCount; i < count; i++) {
- [self initializeLine:i];
- }
-
- [self display];
-
- sprintf(str,"%d", count);
- NXWriteDefault([NXApp appName], "SpermViewCount", str);
-
- return self;
- }
-
- - getSpermCount
- {
- const char *ptr;
- int val;
-
- [spermCountSlider setMinValue: 10];
- [spermCountSlider setMaxValue: MAXCOUNT];
-
- ptr = NXGetDefaultValue([NXApp appName], "SpermViewCount");
- if (ptr)
- {
- sscanf(ptr,"%d",&val);
- if (val >= 10 && val <= MAXCOUNT) count = val;
- else count = MAXCOUNT;
- }
- else count = MAXCOUNT;
-
- return self;
- }
-
- - setUseColor:sender
- {
- useColors = [sender state];
-
- if (useColors)
- NXWriteDefault([NXApp appName], "SpermViewColor", "Yes");
- else
- NXRemoveDefault([NXApp appName], "SpermViewColor");
-
- return self;
- }
-
- - getUseColor
- {
- const char *ptr;
-
- ptr = NXGetDefaultValue([NXApp appName], "SpermViewColor");
-
- if (!ptr || !strcmp(ptr,"No")) useColors = NO;
- else useColors = YES;
-
- return self;
- }
-
- - setLineWidth:sender
- {
- char str[50];
-
- lineWidth = MAX([sender floatValue], 0.0);
- sprintf(str,"%5.1f", lineWidth);
- NXWriteDefault([NXApp appName], "SpermViewWidth", str);
-
- return self;
- }
-
- - getLineWidth
- {
- const char *ptr;
- float val;
-
- [spermWidthSlider setMinValue: 0];
- [spermWidthSlider setMaxValue: 8];
-
- ptr = NXGetDefaultValue([NXApp appName], "SpermViewWidth");
- if (ptr)
- {
- sscanf(ptr,"%f",&val);
- if (val >= 0 && val <= 8) lineWidth = val;
- else lineWidth = 0;
- }
- else lineWidth = 0;
-
- return self;
- }
-
- - sizeTo:(NXCoord)width :(NXCoord)height
- {
- [super sizeTo:width :height];
-
- if (!alreadyInitialized)
- { int i;
- mouse[0] = NX_MIDX(&bounds);
- mouse[1] = NX_MIDY(&bounds);
-
- for (i = 0; i < MAXCOUNT; i++) {
- [self initializeLine:i];
- }
- alreadyInitialized = YES;
- }
-
- [self newWindow];
- return self;
- }
-
- - (const char *)windowTitle
- { return "Sperm";
- }
-
- - (BOOL) useBufferedWindow;
- { return YES;
- }
-
-
- - inspector:sender
- {
- char buf[MAXPATHLEN];
-
- if (!inspectorPanel)
- {
- [NXBundle getPath:buf forResource:"sperm" ofType:"nib" inDirectory:[sender moduleDirectory:"Sperm"] withVersion:0];
- [NXApp loadNibFile:buf owner:self withNames:NO];
-
- [spermCountSlider setIntValue:count];
- [spermWidthSlider setFloatValue:lineWidth];
- [colorButton setState: (useColors ? 1:0)];
- }
- return inspectorPanel;
- }
-
- @end
-